/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2013 University of Colorado Denver
 * <min.choi@ucdenver.edu>
 * <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#include <sxString.h>
#include <sstream>

/*
 * The QString class is used to provide the underlying implementation of this
 * String class. The QString class provides all of the required functionality
 * of this String interface and most of the functions simply use the QString
 * functions to do the heavy lifting. The QVector class is required due to
 * the Unicode spport of the QString.
 */
#include <QString>
#include <QVector>

namespace Sx {

/*
 * The type name StringType is utilized to reduce the number of actual QString
 * type dependencies contained in this implementaiton. To change the underlying
 * implementation string type from QString to another library it can be changed
 * at this location (This assumes that the interface between QString and the
 * new libraries string type will be the same. Realistically it will not be
 * the same and will require additional work throughout the class.
 */
typedef QString StringType;

const static char NULL_CHAR = '\0';
const static char NULL_CHAR_UTF8 = '\0';
const static char16_t NULL_CHAR_UTF16 = 0;
const static char32_t NULL_CHAR_UTF32 = 0;

/*
 * The StringImplemenation is a definition for the foreward declaration
 * defined in the Sx String header. This is the private information about the
 * actual implementation type used to implement the Sx String interface.
 */
struct Detail::StringImplementation {
	StringType string;
};

}

/*
 * Use the Detail namespace so that the implementation code is not cluttered
 * with the namespace. (This is the one exception to the norm of always
 * specifying the namespace).
 */
using namespace Sx::Detail;

Sx::String::String() {
	this->imp = new StringImplementation();
	this->imp->string = StringType();
}

Sx::String::String(const String& string) {
	this->imp = new StringImplementation();
	this->imp->string = string.imp->string;
}

Sx::String::String(const std::string& string) {
	this->imp = new StringImplementation();
	this->imp->string = StringType(string.c_str());
}

Sx::String::String(char c) {
	this->imp = new StringImplementation();
	this->imp->string = StringType(c);
}

Sx::String::String(const char* s) {
	this->imp = new StringImplementation();
	this->imp->string = StringType(s);
}

Sx::String::String(const char32_t* s) {
	this->imp = new StringImplementation();
	this->imp->string = StringType();
	this->imp->string.fromUcs4(s);
}

Sx::String::String(short value) {
	this->imp = new StringImplementation();
	this->imp->string = StringType::number(value);
}

Sx::String::String(int value) {
	this->imp = new StringImplementation();
	this->imp->string = StringType::number(value);
}

Sx::String::String(long value) {
	this->imp = new StringImplementation();
	this->imp->string = StringType::number(value);
}

Sx::String::String(float value) {
	this->imp = new StringImplementation();
	this->imp->string = StringType::number(value);
}

Sx::String::String(double value) {
	this->imp = new StringImplementation();
	this->imp->string = StringType::number(value);
}

Sx::String::~String() {
	this->imp->string.clear();
	delete this->imp;
}

void Sx::String::append(char c) {
	this->imp->string.append(c);
}

void Sx::String::append(const String& string) {
	this->imp->string.append(string.imp->string);
}

void Sx::String::append(const std::string& string) {
	this->imp->string.append(string.c_str());
}

void Sx::String::append(const char* s) {
	this->imp->string.append(s);
}

void Sx::String::append(short value) {
	this->imp->string.append(this->imp->string.number(value));
}

void Sx::String::append(int value) {
	this->imp->string.append(this->imp->string.number(value));
}

void Sx::String::append(long value) {
	this->imp->string.append(this->imp->string.number(value));
}

void Sx::String::append(float value) {
	this->imp->string.append(this->imp->string.number(value));
}

void Sx::String::append(double value) {
	this->imp->string.append(this->imp->string.number(value));
}

void Sx::String::push_back(char c) {
	this->append(c);
}

void Sx::String::push_back(const String& string) {
	this->append(string);
}

void Sx::String::push_back(const char* s) {
	this->append(s);
}

void Sx::String::push_back(short value) {
	this->append(value);
}

void Sx::String::push_back(int value) {
	this->append(value);
}

void Sx::String::push_back(long value) {
	this->append(value);
}

void Sx::String::push_back(float value) {
	this->append(value);
}

void Sx::String::push_back(double value) {
	this->append(value);
}

void Sx::String::push_front(char c) {
	this->imp->string.push_front(c);
}

void Sx::String::push_front(const String& string) {
	this->imp->string.push_front(string.imp->string);
}

void Sx::String::push_front(const char* s) {
	this->imp->string.push_front(s);
}

void Sx::String::push_front(short value) {
	this->imp->string.push_front(StringType::number(value));
}

void Sx::String::push_front(int value) {
	this->imp->string.push_front(StringType::number(value));
}

void Sx::String::push_front(long value) {
	this->imp->string.push_front(StringType::number(value));
}

void Sx::String::push_front(float value) {
	this->imp->string.push_front(StringType::number(value));
}

void Sx::String::push_front(double value) {
	this->imp->string.push_front(StringType::number(value));
}

int Sx::String::compare(const String& string, bool caseSensitive) const {
	if ( caseSensitive == true ) return this->imp->string.compare(string.imp->string, Qt::CaseSensitive);
	return this->imp->string.compare(string.imp->string, Qt::CaseInsensitive);
}

void Sx::String::clear() {
	this->imp->string.clear();
}

void Sx::String::trim() {
	String s = this->trimmed();
	this->imp->string = s.imp->string;
}

bool Sx::String::isInt() const {
	bool ok = false;
	this->imp->string.toInt(&ok);
	return ok;
}

bool Sx::String::isDouble() const {
	bool ok = false;
	this->imp->string.toDouble(&ok);
	return ok;
}

bool Sx::String::isFloat() const {
	bool ok = false;
	this->imp->string.toFloat(&ok);
	return ok;
}

bool Sx::String::isEmpty() const {
	return this->imp->string.isEmpty();
}

void Sx::String::insert(unsigned int index, char c) {
	this->imp->string.insert(index, c);
}

unsigned int Sx::String::length() const {
	return this->imp->string.length();
}

int Sx::String::findFirstInstanceOf(char c) const {
	for ( int i = 0; i < this->imp->string.length(); i++ )
		if ( this->imp->string.at(i) == c ) return i;
	return String::INVALID_STRING_POSITION;
}

bool Sx::String::eraseSubstring(unsigned int start, unsigned int end) {
	if ( start > end || end >= static_cast<unsigned int>(this->imp->string.length()) ) return false;
	unsigned int len = (end - start) + 1;
	this->imp->string.remove(start, len);
	return true;
}

char Sx::String::get(unsigned int index) const {
	if ( this->imp->string.length() == 0 ) return NULL_CHAR;
	if ( index >= static_cast<unsigned int>(this->imp->string.length()) ) return NULL_CHAR;
	return this->toAscii().get(index);
}

char Sx::String::at(unsigned int index) const {
	return this->get(index);
}

char Sx::String::getUTF8(unsigned int index) const {
	if ( this->imp->string.length() == 0 ) return NULL_CHAR;
	if ( index >= static_cast<unsigned int>(this->imp->string.length()) ) return NULL_CHAR;
	return this->toUTF8().get(index);
}

char Sx::String::atUTF8(unsigned int index) const {
	return this->getUTF8(index);
}

char16_t Sx::String::getUTF16(unsigned int index) const {
	if ( this->imp->string.length() == 0 ) return NULL_CHAR;
	if ( index >= static_cast<unsigned int>(this->imp->string.length()) ) return NULL_CHAR;
	return this->toUTF16().get(index);
}

char16_t Sx::String::atUTF16(unsigned int index) const {
	return this->get(index);
}

char32_t Sx::String::getUTF32(unsigned int index) const {
	if ( this->imp->string.length() == 0 ) return NULL_CHAR;
	if ( index >= static_cast<unsigned int>(this->imp->string.length()) ) return NULL_CHAR;
	return this->toUTF32().get(index);
}

char32_t Sx::String::atUTF32(unsigned int index) const {
	return this->get(index);
}

void Sx::String::remove(char c, bool caseSensitive) {
	if ( caseSensitive ) this->imp->string.remove(QChar(c));
	else this->imp->string.remove(QChar(c), Qt::CaseInsensitive);
}

Sx::String Sx::String::toLower() const {
	String s;
	s.imp->string = this->imp->string.toLower();
	return s;
}

Sx::String Sx::String::toUpper() const {
	String s;
	s.imp->string = this->imp->string.toUpper();
	return s;
}

Sx::String Sx::String::substring(unsigned int start, unsigned int end) const {
	String s;
	if ( start > end ) return s;
	unsigned int len = end - start;
	s.imp->string = this->imp->string.mid(start, len + 1);
	return s;
}

Sx::String::operator std::string () {
	return std::string(this->imp->string.toAscii());
}

Sx::Util::ArrayList<char> Sx::String::toAscii(bool null) const {
	Util::ArrayList<char> s(this->imp->string.toAscii(), this->imp->string.length());
	if ( null ) s.addLast(NULL_CHAR);
	return s;
}

Sx::Util::ArrayList<char> Sx::String::toUTF8(bool null) const {
	Util::ArrayList<char> s(this->imp->string.toUtf8(), this->imp->string.length());
	if ( null ) s.addLast(NULL_CHAR_UTF8);
	return s;
}

Sx::Util::ArrayList<char16_t> Sx::String::toUTF16(bool null) const {
	Util::ArrayList<char16_t> s(this->imp->string.utf16(), this->imp->string.length());
	if ( null ) s.addLast(NULL_CHAR_UTF16);
	return s;
}

Sx::Util::ArrayList<char32_t> Sx::String::toUTF32(bool null) const {
	Util::ArrayList<char32_t> s(this->imp->string.toUcs4().constData(), this->imp->string.length());
	if ( null ) s.addLast(NULL_CHAR_UTF32);
	return s;
}

Sx::String& Sx::String::operator = (const char* s) {
	this->imp->string = StringType(s);
	return *this;
}

Sx::String& Sx::String::operator = (const String& string) {
	if ( this == &string ) return *this;
	this->imp->string = string.imp->string;
	return *this;
}

Sx::String& Sx::String::operator += (char c) {
	this->append(c);
	return *this;
}

Sx::String& Sx::String::operator += (const String& string) {
	this->append(string);
	return *this;
}

Sx::String& Sx::String::operator += (short value) {
	this->append(value);
	return *this;
}

Sx::String& Sx::String::operator += (int value) {
	this->append(value);
	return *this;
}

Sx::String& Sx::String::operator += (long value) {
	this->append(value);
	return *this;
}

Sx::String& Sx::String::operator += (float value) {
	this->append(value);
	return *this;
}

Sx::String& Sx::String::operator += (double value) {
	this->append(value);
	return *this;
}

Sx::String Sx::String::operator + (const String& string) {
	String result(*this);
	result.append(string);
	return result;
}

Sx::String operator + (const char* literal, const Sx::String& string) {
	Sx::String result(literal);
	result.append(string);
	return result;
}

bool Sx::String::operator < (const String& string) const {
	if ( this->imp->string.compare(string.imp->string, Qt::CaseSensitive) < 0 ) return true;
	return false;
}

bool Sx::String::operator > (const String& string) const {
	if ( this->imp->string.compare(string.imp->string, Qt::CaseSensitive) > 0 ) return true;
	return false;
}

bool Sx::String::operator <= (const String& string) const {
	if ( this->imp->string.compare(string.imp->string, Qt::CaseSensitive) <= 0 ) return true;
	return false;
}

bool Sx::String::operator >= (const String& string) const {
	if ( this->imp->string.compare(string.imp->string, Qt::CaseSensitive) >= 0 ) return true;
	return false;
}

char Sx::String::operator [] (unsigned int index) {
	return this->get(index);
}

char Sx::String::operator [] (unsigned int index) const {
	return this->imp->string.at(index).toAscii();
}

bool Sx::String::operator == (const String& string) {
	if ( this->imp->string == string.imp->string ) return true;
	return false;
}

bool Sx::String::operator == (const String& string) const {
	if ( this->imp->string == string.imp->string ) return true;
	return false;
}

bool Sx::String::operator != (const String& string) {
	if ( this->imp->string != string.imp->string ) return true;
	return false;
}

Sx::String Sx::String::Trim(const String& string) {
	String s = string;
	s.trim();
	return s;
}

Sx::String Sx::String::ToLower(const String& string) {
	return string.toLower();
}

Sx::String Sx::String::ToUpper(const String& string) {
	return string.toUpper();
}

unsigned int Sx::String::Occurances(const String& string, char c) {
	unsigned int count = 0;
	for ( unsigned int i = 0; i < static_cast<unsigned int>(string.imp->string.length()); i++ )
		if ( string.imp->string[i] == c ) count++;
	return count;
}

Sx::String Sx::String::trimmed() const {
	String s;
	s.imp->string = this->imp->string.trimmed();
	return s;
}

Sx::String Sx::String::ValueOf(short value) {
	return String(value);
}

Sx::String Sx::String::ValueOf(int value) {
	return String(value);
}

Sx::String Sx::String::ValueOf(long value) {
	return String(value);
}

Sx::String Sx::String::ValueOf(float value) {
	return String(value);
}

Sx::String Sx::String::ValueOf(double value) {
	return String(value);
}

Sx::String Sx::String::HexValueOf(int value, bool upper) {
	std::stringstream stream;
	stream << std::hex << value;
	if ( upper ) return String::ToUpper(String(stream.str()));
	return String(stream.str());
}

Sx::String Sx::String::HexValueOf(long value, bool upper) {
	std::stringstream stream;
	stream << std::hex << value;
	if ( upper ) return String::ToUpper(String(stream.str()));
	return String(stream.str());
}

unsigned int Sx::StringHash::operator() (const String& string) const {
	unsigned int hash = string.length();
	for ( unsigned int i = 0; i < string.length(); i++ )
		hash = ((hash << 5) + hash) + string[i];
	return hash;
}

bool Sx::StringEq::operator() (const String& a, const String& b) const {
	if ( a.compare(b, true) ) return true;
	else return false;
}
